package com.martian.service.impl;

import com.martian.mapper.MovieMapper;
import com.martian.mapper.UserMapper;
import com.martian.pojo.entity.Movie;
import com.martian.pojo.entity.MovieStatistic;
import com.martian.pojo.entity.UserStatistic;
import com.martian.pojo.form.MovieRatingForm;
import com.martian.service.ThreadService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;

import java.util.LinkedList;
import java.util.Queue;

import static com.martian.service.impl.UserServiceImpl.getUserWatchHistory;

/**
 * @author martian
 * @version V1.0.0
 * @Description 用于耗时操作的异步执行，包含用户评分电影时更新用户和电影统计表
 * @date 2023-06-21 21:44
 */
@Service
public class ThreadServiceImpl implements ThreadService {
    @Resource
    MovieMapper movieMapper;
    @Resource
    UserMapper userMapper;

    @Override
    @Async("taskExecutor")
    public void updateMovieStat(MovieRatingForm movieRatingForm, Integer updateRatingTag) {
        Integer movieId = movieRatingForm.getMovieId();
        MovieStatistic movieStatistic = movieMapper.selectMovieStatisticsById(movieId);
        if(movieStatistic==null){
            movieStatistic = new MovieStatistic(movieId,1,movieRatingForm.getMovieRating(),0);
            movieMapper.insertMovieStatistic(movieStatistic);
        }else{
            MovieStatistic newStat = movieMapper.selectMovieAvgRating(movieId);
            if (updateRatingTag==1){//表示此次评分是对已有评分进行修改
                movieStatistic = new MovieStatistic(movieId,movieStatistic.getMovieRatingCount(),newStat.getMovieAvgRating(),newStat.getMovieRatingStddev());
            }else{
                movieStatistic = new MovieStatistic(movieId,movieStatistic.getMovieRatingCount()+1,newStat.getMovieAvgRating(),newStat.getMovieRatingStddev());
            }
            //@TODO 若此时更新电影统计表时，其他用户也在更新，则电影评分次数可能会回退
            movieMapper.updateMovieStatistic(movieStatistic);
        }
    }

    @Override
    @Async("taskExecutor")
    public void updateUserStat(Integer userId, Integer updateRatingTag, MovieRatingForm movieRatingForm) {
        UserStatistic userStatistic = userMapper.selectUserStatistics(userId);
        //新用户第一次登陆，需要将数据插入到用户统计表中
        if(userStatistic==null){
            Movie movie = movieMapper.selectMovieById(movieRatingForm.getMovieId());
            String userRatedMovies = movieRatingForm.getMovieId()+"|-1|-1|-1|-1";
            UserStatistic newUserStat = new UserStatistic(userId=userId,userRatedMovies,1,Integer.valueOf(movie.getMovieReleasedYear()),0,movieRatingForm.getMovieRating(),0,movie.getMovieGenres());
            userMapper.insertUserStatistic(newUserStat);
        }else{
            if (updateRatingTag==1){//表示用户是修改评分
                UserStatistic newUserStat = userMapper.selectUserAvgRatingAndStd(userId);
                userMapper.updateUserStatisticRating(newUserStat);
                return;
            }
            Integer ratingCount = userStatistic.getUserRatingCount() + 1;
            //这里表示用户是首次给该电影评分
            userStatistic.setUserRatedMovies(updateUserRatedMovieString(userStatistic.getUserRatedMovies(),movieRatingForm.getMovieId()));
            //@TODO 如果给该方法加上事物，这里是否能一致
            UserStatistic newRatingStat = userMapper.selectUserAvgRatingAndStd(userId);
            UserStatistic statYear = movieMapper.selectUserRatingAvgYearAndStd(userId);
            //@TODO 这个地方暂时没有检查线程安全
            userStatistic.setUserRatingCount(ratingCount);
            userStatistic.setUserRatingStddev(newRatingStat.getUserRatingStddev());
            userStatistic.setUserAvgRating(newRatingStat.getUserAvgRating());
            userStatistic.setUserAvgReleasedYear(statYear.getUserAvgReleasedYear());
            userStatistic.setUserReleasedYearStddev(statYear.getUserReleasedYearStddev());
            userStatistic.setUserLikeGenres(getUserWatchHistory(userMapper.getWatchHistory(userId)));
            userMapper.updateUserStatistic(userStatistic);
        }
    }

    @Override
    //@TODO 当用户已评分四个电影时，第五个电影序列出错。 已解决
    /**
     * oldRated:318|293|148|-1|-
     * 该方法用于更新用户最近五次的评分电影ID序列，中间用|分割，可以重复
     */
    public String updateUserRatedMovieString(String oldRated, Integer newMovieId) {
        String[] ratedMovies = oldRated.split("\\|");
        Queue<String> ratedMoviesQueue = new LinkedList();
        for (String movieId : ratedMovies) {
            if (movieId.equals("-1")){//当遇到-1时，只替换第一个遇到的-1
                ratedMoviesQueue.offer(String.valueOf(newMovieId));
                break;
            }else{
                ratedMoviesQueue.offer(movieId);
            }
        }
        int size = ratedMoviesQueue.size();
        //用户至少点赞过5个电影,这里有两种情况，一、用户此次评分恰好达到五次；二、用户此次评分前已达到五次
        if (size==5){
            if (!ratedMoviesQueue.contains(String.valueOf(newMovieId))) {//此时的电影序列没有-1，但可能新电影ID已经加入；若已经加入，则不作处理
                ratedMoviesQueue.offer(String.valueOf(newMovieId));//将新评分的电影ID加入队列
                ratedMoviesQueue.poll();//弹出队列中评分最早的电影ID
            }
        }else if (ratedMoviesQueue.size()<5){//用户点赞少于5个电影
            for(int i=0;i<5-size;i++){
                ratedMoviesQueue.offer("-1");
            }
        }
        String userRatedMovies = StringUtils.join(ratedMoviesQueue,"|");
        return userRatedMovies;
    }
}
